﻿using Renci.SshNet;
using System.Net.WebSockets;
using System.Text.Json;
using System.Text;
using FunArts.EasyDevOps.Shared.Models.SshWebSocket.Request;
using FunArts.EasyDevOps.Shared.Models.SshWebSocket.Enums;
using Microsoft.Extensions.Hosting;
using FunArts.EasyDevOps.Shared.Models.SshWebSocket.Response;
using FunArts.EasyDevOps.Shared.Models.SshClient;
using System.Text.RegularExpressions;
using FunArts.EasyDevOps.Shared.Models.SshClient.Command;
using FunArts.EasyDevOps.Shared.Models.Enums;
using FunArts.EasyDevOps.Shared.Utils;

namespace FunArts.EasyDevOps.Server.Middleware
{
    public class SshWebSocketMiddleware
    {
        private SshClient _sshClient;
        private SftpClient _sftpClient;

        public async Task HandleWebSocketConnection(HttpContext context, WebSocket webSocket)
        {
            var buffer = new byte[1024 * 4];
            var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

            while (!result.CloseStatus.HasValue)
            {
                var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                var request = JsonSerializer.Deserialize<SshWebSocketRequest<object>>(message);

                string response;
                switch (request.Type)
                {
                    case SshWebSocketRequestTypeEnum.Connect:
                        SshWebSocketRequest<ConnectRequest> connectRequest= JsonSerializer.Deserialize<SshWebSocketRequest<ConnectRequest>>(message);
                        response = HandleConnect(connectRequest.SshRequest);
                        break;
                    case SshWebSocketRequestTypeEnum.ExecuteCommand:
                        SshWebSocketRequest<ExecuteCommandRequest> executeCommandRequest = JsonSerializer.Deserialize<SshWebSocketRequest<ExecuteCommandRequest>>(message);
                        response = HandleExecuteCommand(executeCommandRequest.SshRequest);
                        break;
                    case SshWebSocketRequestTypeEnum.UploadFile:
                        SshWebSocketRequest<UploadFileRequest> uploadFileRequest = JsonSerializer.Deserialize<SshWebSocketRequest<UploadFileRequest>>(message);
                        response = HandleUploadFile(uploadFileRequest.SshRequest);
                        break;
                    case SshWebSocketRequestTypeEnum.DownloadFile:
                        SshWebSocketRequest<DownloadFileRequest> downloadFileRequest = JsonSerializer.Deserialize<SshWebSocketRequest<DownloadFileRequest>>(message);
                        response = HandleDownloadFile(downloadFileRequest.SshRequest);
                        break;
                    case SshWebSocketRequestTypeEnum.LoadDirectoryTree:
                        SshWebSocketRequest<LoadDirectoryTreeRequest> loadDirectoryTreeRequest = JsonSerializer.Deserialize<SshWebSocketRequest<LoadDirectoryTreeRequest>>(message);
                        response = HandleLoadDirectoryTree(loadDirectoryTreeRequest.SshRequest);
                        break;
                    default:
                        response = JsonSerializer.Serialize(new { Error = "无效的消息类型" });
                        break;
                }

                await webSocket.SendAsync(Encoding.UTF8.GetBytes(response), WebSocketMessageType.Text, true, CancellationToken.None);

                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            }

            await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
        }

        private string HandleConnect(ConnectRequest request)
        {
            _sshClient = new SshClient(request.ConnectInfo.Host, request.ConnectInfo.User, request.ConnectInfo.Pwd);
            _sftpClient=new SftpClient(request.ConnectInfo.Host, request.ConnectInfo.User, request.ConnectInfo.Pwd);
            try
            {
                _sshClient.Connect();
                _sftpClient.Connect();
                if (_sshClient.IsConnected && _sftpClient.IsConnected)
                {
                    var sshCommand = _sshClient.CreateCommand(new SystemInfoCommand().BuildCommand(SystemVersionTypeEnum.CentOS));
                    var result = sshCommand.Execute();
                    if (!sshCommand.Error.IsNullOrWhiteSpace())
                    {
                        return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData("获取系统信息出错"));
                    }
                    foreach (var item in result.Split('\n'))
                    {
                        if (item.StartsWith("NAME="))
                        {
                            var index = item.IndexOf("\"") + 1;
                            var systemVersion = item.Substring(index, item.LastIndexOf("\"") - index);
                            if (systemVersion == "Ubuntu")
                            {
                                request.ConnectInfo.SystemVersionType = EasyDevOps.Shared.Models.Enums.SystemVersionTypeEnum.Ubuntu;
                            }
                        }
                    }
                    return JsonSerializer.Serialize(SshWebSocketResponse<ConnectResponse>.SuccessData(new ConnectResponse() { ConnectInfo = request.ConnectInfo }));
                }
                else
                {
                    return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData("连接服务器失败"));
                }
            }
            catch (Exception ex)
            {
                return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData(ex.Message));
            }
        }

        private string HandleExecuteCommand(ExecuteCommandRequest request)
        {
            try
            {
                var sshCommand = _sshClient.CreateCommand(request.Command);
                var result = sshCommand.Execute();
                var response = SshWebSocketResponse<ExecuteCommandResponse>.SuccessData(new ExecuteCommandResponse()
                {
                    Output = result,
                    ExitStatus = sshCommand.ExitStatus,
                    Error = sshCommand.Error
                });
                return JsonSerializer.Serialize(response);
            }
            catch (Exception ex)
            {
                return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData(ex.Message));
            }
        }

        private string HandleUploadFile(UploadFileRequest request)
        {
            try
            {
                using var stream = new MemoryStream(request.FileData);
                _sftpClient.UploadFile(stream, request.RemoteFilePath);
                return JsonSerializer.Serialize(SshWebSocketResponse<object>.SuccessData());
            }
            catch (Exception ex)
            {
                return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData(ex.Message));
            }
        }

        private string HandleDownloadFile(DownloadFileRequest request)
        {
            try
            {
                using var stream = new MemoryStream();
                _sftpClient.DownloadFile(request.RemoteFilePath, stream);
                var response = SshWebSocketResponse<DownloadFileResponse>.SuccessData();
                return JsonSerializer.Serialize(response);
            }
            catch (Exception ex)
            {
                return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData(ex.Message));
            }
        }

        private string HandleLoadDirectoryTree(LoadDirectoryTreeRequest request)
        {
            try
            {
                var command = _sshClient.CreateCommand($"ls -l {request.DirectoryNode.FullPath}");
                var result = command.Execute();

                var lines = result.Split('\n');

                foreach (var line in lines)
                {
                    if (!string.IsNullOrWhiteSpace(line))
                    {
                        var match = Regex.Match(line, @"^([d-])([rwx-]+)\s+\d+\s+\w+\s+\w+\s+\d+\s+\w+\s+\d+\s+[\d:]+\s+(.+)$");
                        if (match.Success)
                        {
                            bool isDir = match.Groups[1].Value == "d";
                            string fileName = match.Groups[3].Value;

                            var dirPath = $"{request.DirectoryNode.FullPath}/{fileName}";
                            var childNode = new DirectoryNode
                            {
                                Name = fileName,
                                FullPath = dirPath,
                                IsDir = isDir,
                                Children = new List<DirectoryNode>()
                            };
                            request.DirectoryNode.Children.Add(childNode);
                        }
                    }
                }

                return JsonSerializer.Serialize(SshWebSocketResponse<LoadDirectoryTreeResponse>.SuccessData(new LoadDirectoryTreeResponse() { DirectoryNode=request.DirectoryNode}));
            }
            catch (Exception ex)
            {
                return JsonSerializer.Serialize(SshWebSocketResponse<object>.ErrorData(ex.Message));
            }
        }
    }
}
